package com.tmall.testgen.common.util;



import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.Collection;
import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;

public class Comparator {
    
    public   boolean compare(Object obj1,Object obj2){
        if(obj1==null&&obj2==null)
            return true;
        
        if(obj1==null||obj2==null)
            return false;
        
        if(obj1.getClass()!=obj2.getClass())
            return false;
        
        
        if(obj1 instanceof Collection){
            return compareCollection((Collection)obj1,(Collection)obj2);
        }else if(obj1 instanceof Map){
            return compareMap((Map)obj1,(Map)obj2);
        }else{
            return compareObject(obj1,obj2);
        }
    }
    
    
    private  boolean compareBasicType(Object obj1,Object obj2){
        if((obj1 instanceof Date)&&(obj2 instanceof Date)){
            Date d1=(Date) obj1;
            Date d2=(Date) obj2;
            return d1.getTime()==d2.getTime();
        }
        
        return obj1.equals(obj2);
    }
    
    private  boolean compareCollection(Collection c1,Collection c2){
        if(c1.size()!=c2.size())
            return false;
        
        Iterator c1Iterator=c1.iterator();
        Iterator c2Iterator=c2.iterator();
        
        while(c1Iterator.hasNext()){
            Object o1=c1Iterator.next();
            Object o2=c2Iterator.next();
            
            if(!compare(o1,o2))
                return false;
        }
        
        return true;
    }
    
    private  boolean compareObject(Object obj1,Object obj2){
        
        if(isBasicType(obj1))
            return compareBasicType(obj1,obj2);
        
        Map<String,Object>   obj1FiledValueMap=getFieldMap(obj1);
        Map<String,Object>   obj2FiledValueMap=getFieldMap(obj2);
        if(obj1FiledValueMap.size()==0&&obj2FiledValueMap.size()==0)
            return true;
        if(obj1FiledValueMap.size()==0||obj2FiledValueMap.size()==0){
            return false;
        }
        
        for(String fieldName:obj1FiledValueMap.keySet()){
            if(!compare(obj1FiledValueMap.get(fieldName),obj2FiledValueMap.get(fieldName)))
                return false;
        }
        return true;
    }
    
    private  boolean compareMap(Map m1,Map m2){
        if(m1.size()!=m2.size())
            return false;
        
        for(Object key:m1.keySet()){
            if(!compare(m1.get(key),m2.get(key)))
                return false;
            
        }
        
        return true;
    }
    
    private   Map<String,Object>  getFieldMap(Object obj){
        
        Class objClass=obj.getClass();
        Map<String,Object>fildValueMap=new HashMap<String,Object>();
        
        Map<String,String> fieldNames=new HashMap<String,String>();
        for(; objClass != Object.class&&objClass!=null ; objClass = objClass.getSuperclass()) {  
            Field[] fields=objClass.getDeclaredFields();
            for(Field field:fields){
                fieldNames.put(field.getName().toLowerCase(),field.getName());
            }
            
        }  
        objClass=obj.getClass();
        for(; objClass != Object.class&&objClass!=null  ; objClass = objClass.getSuperclass()) {  
            Method[] methods=objClass.getDeclaredMethods();
            
            for(Method m:methods){
                String name=m.getName().toLowerCase();
                if(name.startsWith("get")||name.startsWith("is")){
                    boolean isGet=name.startsWith("get");
                    if(isGet&&name.length()==3)
                        continue;
                    String fieldName="";//=isGet?name.substring(3):name.substring(2);
                    
                    if(isGet){
                        fieldName=name.substring(3);
                    }else{
                        fieldName=fildValueMap.get(name)!=null?name:name.substring(2);
                    }
                    
                    if(fieldNames.get(fieldName)!=null){
                        try{
                            Field f=obj.getClass().getDeclaredField(fieldNames.get(fieldName));
                            f.setAccessible(true);
                            Object fv=f.get(obj);
                            fildValueMap.put(fieldName,fv);
                        }catch(Exception e){
                            e.printStackTrace();
                        }
                        
                    }
                }
            }
            
        }  
        
        return fildValueMap;
    }
    private static boolean isBasicType(Object o){
        if(o instanceof String )
            return true;
        
        if(o instanceof Boolean )
            return true;
        if(o instanceof Byte )
            return true;
        if(o instanceof Short )
            return true;
        if(o instanceof Integer )
            return true;
        if(o instanceof Long )
            return true;
        if(o instanceof Float )
            return true;
        if(o instanceof Double )
            return true;
        if(o instanceof Date)
            return true;
        
        return false;
        
    }

}